package com.vlyman.shiro.realm;

import com.vlyman.builder.QueryWrapperBuilder;
import com.vlyman.consts.KeyConsts;
import com.vlyman.exception.ShiroException;
import com.vlyman.rtn.SystemRtn;
import com.vlyman.shiro.entity.SysPrivilege;
import com.vlyman.shiro.entity.SysUser;
import com.vlyman.shiro.jwt.JwtToken;
import com.vlyman.shiro.jwt.JwtUtil;
import com.vlyman.shiro.service.SysPrivilegeService;
import com.vlyman.shiro.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 类描述：
 *  shiro身份校验核心类
 * @author chenjs
 * @date 2018/12/19 16:47
 **/
@Slf4j
public class VlymanRealm extends AuthorizingRealm{

    @Autowired
    private SysUserService sysUserServiceImpl;
    @Autowired
    private SysPrivilegeService sysPrivilegeServiceImpl;

    /**
     * 重写supports方法
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 方法描述：
     *   获取授权信息
     *  @author chenjs
     *  @param principalCollection
     *  @return AuthorizationInfo
     *  @date 2018/12/19 18:00
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("shiro——权限认证");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        String jwtToken = principalCollection.toString();
        try {
            Integer userId = Integer.valueOf(JwtUtil.getAESEncryptClaim(jwtToken, KeyConsts.JWT_CLAIM_USERID));
            List<Map<String, String>> permissonList = sysPrivilegeServiceImpl.getPermissionByUserId(userId);
            Set<String> permissions = new HashSet<>();
            permissonList.forEach(item -> permissions.add(item.get(SysPrivilege.ACCESS)
                .concat("-")
                .concat(item.get(SysPrivilege.ACCESSVALUE))
            ));
            info.setStringPermissions(permissions);
        } catch (ShiroException e) {
            log.error(SystemRtn.TOKEN_DECODE_ERROR.getMsg(), e);
        }
        return info;
    }

    /**
     * 方法描述：
     *   获取身份验证信息
     *  @author chenjs
     *  @param authenticationToken  用户身份信息token
     *  @return 返回封装了用户信息的AuthenticationInfo实例
     *  @date 2018/12/19 17:47
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("shiro——身份认证");
        String jwtToken = (String) authenticationToken.getCredentials();
        String userId;
        try {
            // 解密获得login，用于和数据库进行对比
            userId = JwtUtil.getAESEncryptClaim(jwtToken, KeyConsts.JWT_CLAIM_USERID);
        } catch (ShiroException e) {
            throw new AccountException(SystemRtn.TOKEN_DECODE_ERROR.getCodeStr());
        } catch (Exception e) {
            throw new AccountException(SystemRtn.RSA_DECODE_ERROR.getCodeStr());
        }
        if (!StringUtils.hasText(userId)){
            log.error("Token无法解析出login！");
            throw new AccountException(SystemRtn.TOKEN_INVALID.getCodeStr());
        }
        //从数据库获取对应用户名的用户
        SysUser user = sysUserServiceImpl.getOne(
            QueryWrapperBuilder.init(SysUser.class)
                .eq(SysUser.ID, Integer.valueOf(userId))
        );
        if (user == null) {
            //该帐号不存在
            throw new AccountException(SystemRtn.NAME_INVALID.getCodeStr());
        }
        // 开始认证，要AccessToken认证通过
        if (JwtUtil.verify(jwtToken)) {
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                    jwtToken, jwtToken, getName());
            return authenticationInfo;
        }
        throw new AccountException(SystemRtn.TOKEN_VALIDATE_ERROR.getCodeStr());
    }
}
